#ifndef COMMON_ELASTIC_H
#define COMMON_ELASTIC_H

#include <elasticlient/client.h>
#include <json/json.h>

#include <memory>
#include <string>
#include <vector>

namespace chat {
/**
 * @brief 创建 ES 索引类
 */
class ESIndex {
   public:
    ESIndex(std::shared_ptr<elasticlient::Client> client,
            const std::string& index_name, const std::string& index_type);

    /**
     * @brief 向 properties_ 添加字段映射
     *
     * @param key[in]: 映射名称
     * @param type[in]: 映射类型 - text
     * @param analyzer[in]: 分词器 - ik_max_word
     * @param enabled[in]: 默认为true, false - 仅作存储，不做搜索和分析
     *
     * @note  例如:"user_id": {
        "type": "keyword",
        "analyzer": "standard"
      },
     */
    bool append(const std::string& key, const std::string& type,
                const std::string& analyzer, bool enabled);

    /**
     * @brief 创建index索引，并发起请求
     */
    bool create();

   private:
    std::shared_ptr<elasticlient::Client> client_ = nullptr;  // ES 客户端

    Json::Value index_;       // ES 索引
    Json::Value properties_;  // 所有的映射存放在这个字段中
    std::string index_name_;  // 索引名称
    std::string index_type_;  // 索引类型
};

/**
 * @brief 添加/修改 ES数据 类
 */
class ESInsert {
   public:
    ESInsert(std::shared_ptr<elasticlient::Client> client,
             const std::string& index_name, const std::string& index_type);

    /**
     * @brief 向 item_ 中添加json数据
     */
    bool append(const std::string& key, const std::string& val);

    /**
     * @brief 发起请求，将 item_ 插入到索引中或者修改id的数据
     *
     * @param id[in]: 每个 item_ 的唯一id
     */
    bool insert(const std::string& id);

   private:
    std::shared_ptr<elasticlient::Client> client_ = nullptr;  // ES 客户端

    Json::Value item_;        // 要往索引中添加的数据
    std::string index_name_;  // 索引名称
    std::string index_type_;  // 索引类型
};

class ESRemove {
   public:
    ESRemove(std::shared_ptr<elasticlient::Client> client,
             const std::string& index_name, const std::string& index_type);

    /**
     * @brief 发起请求，将 索引中的 id 字段删除
     *
     * @param id[in]: 每个 item_ 的唯一id
     */
    bool remove(const std::string& id);

   private:
    std::shared_ptr<elasticlient::Client> client_ = nullptr;  // ES 客户端

    std::string index_name_;  // 索引名称
    std::string index_type_;  // 索引类型
};

class ESSearch {
   public:
    ESSearch(std::shared_ptr<elasticlient::Client> client,
             const std::string& index_name, const std::string& index_type);

    /**
     * @brief 向过滤条件中添加精确匹配字段
     *
     * @param key[in]: 精确匹配字段的 key 值
     * @param vals[in]: 一个key可对应多个val，故用数组存储
     */
    bool appendMustNotTerm(const std::string& key,
                           const std::vector<std::string>& vals);

    /**
     * @brief 向包含条件中添加分词匹配字段
     */
    bool appendShouldMatch(const std::string& key, const std::string& val);

    /**
     * @brief 向必须包含条件中添加精确匹配字段
     */
    bool appendMustTerm(const std::string& key, const std::string& val);

    /**
     * @brief 向必须包含条件中添加分词匹配字段
     */
    bool appendMustMatch(const std::string& key, const std::string& val);

    /**
     * @brief 根据 must_not_ 和 should_ 构造搜索请求，返回搜索结果
     *
     * @return search 的响应结果
     */
    Json::Value search();

   private:
    std::shared_ptr<elasticlient::Client> client_ = nullptr;  // ES 客户端

    std::string index_name_;  // 索引名称
    std::string index_type_;  // 索引类型
    Json::Value must_not_;    // 必须不包含 - 过滤条件
    Json::Value should_;      // 应该包含
    Json::Value must_;        // 必须包含
};
}  // namespace chat

#endif  // COMMON_ELASTIC_H